<template>
    <div class="page flex-column" v-loading="loading">
        <div class="page-body flex-row flex1">
            <div class="left-container flex-column">
                <div class="left-btn flex-row flex-item">
                    <el-radio-group v-model="network" size="small" @change="refresh">
                        <el-radio-button v-for="item in networks" :label="item.value" :key="item.value">{{ item.label }}</el-radio-button>
                    </el-radio-group>
                </div>
                <br>
                <el-input
                        size="small"
                        placeholder="输入关键字进行过滤"
                        clearable
                        v-model="filterText">
                </el-input>
                <br>
                <el-tree
                        ref="nodeTree"
                        class="filter-tree"
                        :data="nodeTree"
                        node-key="id"
                        :props="defaultProps"
                        default-expand-all
                        :filter-node-method="filterNode"
                        @node-click="nodeClick">
                         <div class="custom-tree-node flex-row flex-item" :class="{'select-node': currentNet.id === data.id}" slot-scope="{ node, data }">
                            <span>{{ node.label }}</span>
                          </div>
                </el-tree>
            </div>
            <div class="right-container flex-column flex1">
                <topology-map ref="topologyMap"
                              @deleteNode="deleteNode"
                              @editNode="openEditNode"
                              @addNode="addNode"
                              @moveNode="saveNode"
                              @refresh="queryLocalNodeTree"
                ></topology-map>
                <div class="parent-tips tip-container flex-column" @dragover="containerOver">
                    <p class="item-title">操作</p>
                    <div @click="showResult" class="tip-item flex-row flex-center">
                        <img class="device-icon" :draggable="false" src="./img/analysis.png" alt="" style="cursor: pointer">
                        <p class="icon-name">分析</p>
                    </div>
                    <div class="tip-item flex-row flex-center can-drag"
                         draggable="true"
                         @dragstart="dragstart({value: 'item'})"
                         @dragend="e => dragend({value: 'line'}, e)">
                        <img class="device-icon"
                             src="./img/line.png" alt="">
                        <p class="icon-name">连线</p>
                    </div>
                    <p class="item-title">添加</p>
                    <el-popover v-for="item in deviceTypeList"
                            :key="item.id"
                            placement="left"
                            trigger="hover">
                        <div class="child-tips flex-column">
                            <template v-if="item.children && item.children.length">
                                <div v-for="child in item.children" :key="child.id"
                                     class="tip-item flex-row flex-center can-drag"
                                     draggable="true"
                                     @dragstart="dragstart(child)"
                                     @dragend="e => dragend(child, e)">
                                    <img class="device-icon"
                                         :src="getDeviceIcons(child.key)" alt="">
                                    <p class="icon-name">{{child.keyName}}</p>
                                </div>
                            </template>
                            <p v-else class="no-data">没有子集分类</p>
                        </div>
                        <div slot="reference" class="tip-item flex-row flex-center">
                            <img class="device-icon"
                                 :src="getDeviceIcons(item.key)" alt="">
                            <p class="icon-name">{{item.keyName}}</p>
                        </div>
                    </el-popover>
                </div>
            </div>
        </div>
        <el-dialog
                title="分析报告"
                :visible.sync="resultVisible"
                width="600px">
            <tz-table
                    ref="reportTable"
                    highlight-current-row
                    :pagination="false"
                    size="mini"
                    :data="loadData">
                <el-table-column
                        prop="analysisName"
                        show-overflow-tooltip
                        label="报告名称">
                </el-table-column>
                <el-table-column
                        prop="createTime"
                        show-overflow-tooltip
                        label="评估时间"
                        :formatter="(row) => {
                       return moment(row.createTime).format('YYYY-MM-DD HH:mm:ss')
                     }">
                </el-table-column>
                <el-table-column
                        prop="tag"
                        label="操作">
                    <template slot-scope="scope">
                        <el-button @click="toDetail(scope)" size="small" type="text">查看</el-button>
                        <el-button @click="deleteResult(scope.row.id)" size="small" type="text">删除</el-button>
                    </template>
                </el-table-column>
            </tz-table>
            <span slot="footer" class="dialog-footer">
            <el-button :loading="loading" size="small" type="primary" @click="doAnalysis">生成分析报告</el-button>
            <el-button size="small" @click="resultVisible = false">确 定</el-button>
          </span>
        </el-dialog>
        <el-dialog :visible.sync="editVisible"
                   :title="deviceItem.id ? '编辑节点' : '新增节点'"
                   width="500px">
            <el-form ref="nodeForm" v-loading="saveLoading" label-position="left" label-width="80px" :model="deviceItem" :rules="rules">
                <el-form-item label="节点名称" prop="nodeName">
                    <el-input :maxlength="30" show-word-limit size="small" v-model="deviceItem.nodeName"></el-input>
                </el-form-item>
                <el-form-item label="设备分类" prop="deviceTypes">
                    <el-cascader
                        size="small"
                        disabled
                         style="width: 100%"
                        v-model="deviceItem.deviceTypes"
                        :options="deviceTypeList"
                        :props="{
                                value: 'key',
                                label: 'keyName'
                            }"></el-cascader>
                </el-form-item>
                <el-form-item v-if="deviceItem.deviceTypes.length" label="设备ip" prop="ip">
                    <el-input :maxlength="100" show-word-limit size="small" placeholder="请输入设备ip地址" v-model="deviceItem.ip" @input="ipChange">
                    </el-input>
                    <p class="error-msg">{{errorMsg}}</p>
                </el-form-item>
                <el-form-item v-if="deviceItem.code" label="Mac地址" prop="nodeMac">
                    <el-input :maxlength="100" show-word-limit size="small" v-model="deviceItem.nodeMac"></el-input>
                </el-form-item>
                <el-form-item v-if="deviceItem.ip" label="vlan" prop="vlan">
                    <el-input :maxlength="100" show-word-limit size="small" v-model="deviceItem.vlan"></el-input>
                </el-form-item>
                <el-form-item v-if="deviceItem.remarks" label="其他设备">
                    <el-tag type="success"
                            v-for="(item, index) in deviceItem.remarks.split('_,')"
                            :key="index">
                        {{ item }}
                    </el-tag>
                </el-form-item>
                <div class="flex-row">
                    <div class="flex1"></div>
                    <el-button size="small" @click="cancel">取 消</el-button>
                    <el-button size="small" @click="saveNode({...deviceItem}, true)" type="primary">确 定</el-button>
                </div>
            </el-form>
        </el-dialog>
    </div>
</template>

<script>
import { networks } from '@/assets/dict'
import topologyMap from './topologyMap'
import tzTable from '@/components/tz-table'
import moment from 'moment'
import { mapState } from 'vuex'
const isValidIPv4 = (ip) => {
  return /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(ip);
}
const initDeviceData = {
  id: '',
  code: '',
  deviceTypes: [],
  ip: '',
  nodeMac: '',
  nodeName: '',
  vlan: '',
  remarks: ''
}
export default {
  name: 'topology',
  components: { topologyMap, tzTable },
  data() {
    return {
      rules: {
        nodeName: [
          { required: true, message: '请填写节点名称', trigger: 'blur' }
        ],
        deviceTypes: [
          { required: true, message: '请选择设备分类', trigger: 'change' }
        ],
        vlan: [
          { required: true, message: '请输入vlan', trigger: 'blur' }
        ],
        ip: [
          { required: true, message: '请输入ip', trigger: 'blur' },
          { validator: (rule, value, callback) => {
            if (!value) {
              callback();
            } else if (!isValidIPv4(value)) {
              return callback(new Error('ip地址不合法'));
            } else {
              callback();
            }
          }, trigger: 'blur' }
        ]
      },
      deviceItem: {
        ...initDeviceData
      },
      moment,
      loading: false,
      network: 'JZW',
      networks,
      filterText: '',
      nodeTree: [],
      defaultProps: {
        label: 'internetName',
        children: 'children'
      },
      currentNet: null,
      addType: null,
      resultVisible: false,
      editVisible: false,
      deviceLoading: false,
      saveLoading: false,
      deviceList: [],
      errorMsg: '',
      // 标记是否拖拽到菜单容器上方
      dragOverMenu: false
    };
  },
  computed: {
    ...mapState('dict', ['deviceTypeList', 'getDeviceIcons', 'deviceTypeMap']),
  },
  methods: {
    async ipChange(ip) {
      if (isValidIPv4(ip)) {
        this.deviceItem.code = ''
        this.deviceItem.nodeMac = ''
        this.deviceItem.vlan = ''
        this.deviceItem.remarks = ''
        this.deviceItem.vlan = ip.split('.')[2]
        this.saveLoading = true
        await this.getNodeByIp(ip)
        this.saveLoading = false
      }
    },
    getNodeByIp(ip) {
      let isAll = 0
      if (this.deviceItem.id) {
        isAll = 1
      }
      this.errorMsg = ''
      return this.$http.get('/device/getDeviceListByNode', {
        params: {
          ip,
          deviceType: this.deviceItem.deviceTypes[0],
          deviceTypeTwo: this.deviceItem.deviceTypes[1],
          network: this.network,
          size: 999,
          isAll
        }
      }).then(res => {
        this.deviceList = res.records || []
        const item = this.deviceList.shift()
        const { deviceTypes, id } = this.deviceItem
        if (item) {
          this.deviceItem = {
            ...this.deviceItem,
            ...item,
            nodeName: item.name,
            deviceTypes,
            id,
            ip,
            remarks: this.deviceList.map(item => {
              return `type:${item.deviceTypes.join(',')}, code:${item.code}`
            }).join('_,')
          }
        } else {
          if (isAll) {
            this.errorMsg = `没有IP地址为${ip}的${this.deviceTypeMap[this.deviceItem.deviceTypes[1]]}`
          } else {
            this.errorMsg = `没有IP地址为${ip}且未绑定的${this.deviceTypeMap[this.deviceItem.deviceTypes[1]]}`
          }
        }
      }).catch(err => {
        this.$TZmessage.error(err.msg || '获取设备信息失败')
      })
    },
    addNode(node) {
      this.openEditNode(node)
    },
    openEditNode(node) {
      this.deviceItem = {
        ...node,
        deviceTypes: [node.deviceType, node.deviceTypeTwo]
      }
      console.log(this.deviceItem)
      this.errorMsg = ''
      this.editVisible = true
    },
    saveNode(nodeData, formValidate) {
      const saveRequire = async(node) => {
        try {
          if (!node.code && formValidate) {
            await this.$confirm('当前节点未绑定设备，是否保存？')
          }
          this.saveLoading = true
          this.$http.post('/topu/saveOrUpdateNode', {
            ...node,
            network: this.network,
            localInternetId: this.currentNet.id
          // }).then(res => {
          }).then(() => {
            this.queryLocalNodeTree()
            this.editVisible = false
          }).catch(err => {
            this.$TZmessage.error(err.msg || '保存节点数据失败')
          }).finally(() => {
            this.saveLoading = false
          })
        } catch (e) {
          console.log('取消保存')
        }
      }
      if (formValidate) {
        this.$refs.nodeForm.validate((valid) => {
          if (valid) {
            saveRequire(nodeData)
          }
          return false
        })
      } else {
        saveRequire(nodeData)
      }
    },
    doAnalysis() {
      this.loading = true
      // this.$TZmessage.success('报告生成中，预计十分钟后生成结果，请稍后查看！')
      // this.$http.get(`/topu/analysisTopus/${this.currentNet.id}`).then(res => {
      this.$http.get(`/topu/analysisTopus/${this.currentNet.id}`).then(() => {
        this.$refs.reportTable.refresh(true)
      }).catch(err => {
        this.$TZmessage.error(err.msg || '操作失败，请检查拓扑中是否包含有效节点')
      }).finally(() => {
        this.loading = false
      })
    },
    showResult() {
      this.resultVisible = true
      this.$refs.reportTable?.refresh(true)
    },
    loadData() {
      return this.$http.get('/topu/getListAnalysis', {
        params: {
          localInternetId: this.currentNet.id,
        }
      }).then(res => {
        return {
          data: res || [],
        }
      }).catch(err => {
        this.$TZmessage.error(err.message || '获取列表失败')
      })
    },
    toDetail({row}) {
      this.$router.push({
        path: '/operation/result-info',
        query: {
          analysisId: row.id,
          type: 'topo',
          taskName: row.taskName,
          createTime: row.createTime ? moment(row.createTime).format('YYYY-MM-DD HH:mm:ss') : '-'
        }
      })
    },
    deleteResult(id) {
      // this.$confirm('是否删除当前任务').then(res => {
      this.$confirm('是否删除当前任务').then(() => {
        this.$http.delete('/topu/deleteAnalysis', {
          params: {
            id
          }
        }).then(() => {
          this.$TZmessage.success('删除成功')
          this.$refs.reportTable?.refresh(true)
        }).catch(err => {
          this.$TZmessage.error(err.msg || '删除失败')
        })
      // }).catch(err => {
      }).catch(() => {
        console.log('取消执行')
      })
    },
    dragstart(item) {
      this.addType = item.value
    },
    dragend(item, e) {
      this.dragOverMenu = false
      setTimeout(() => {
        if (!this.dragOverMenu) {
          const parent = this.deviceTypeList.find(p => {
            return p.id === item.pid
          })
          this.$refs.topologyMap.addItem({
            ...item,
            deviceType: parent?.key,
            deviceTypeTwo: item?.key
          }, e)
        }
      }, 100)
    },
    containerOver() {
      setTimeout(() => {
        this.dragOverMenu = true
      }, 50)
    },
    async refresh() {
      this.loading = true
      await this.queryNodeTree()
      this.loading = false
    },
    async nodeClick(data) {
      this.currentNet = {...data}
      this.loading = true
      await this.queryLocalNodeTree()
      this.loading = false
    },
    queryLocalNodeTree() {
      // this.loading = true
      return this.$http.get('/topu/getTopuList', {
        params: {
          localInterId : this.currentNet.id
        }
      }).then(res => {
        const list = res || []
        let minX = 0
        let minY = 0
        if (list.length) {
          minX = Math.min(...list.map(item => item.x)) || 0
          minY = Math.min(...list.map(item => item.y)) || 0
        }
        const nodeList = list.map(item => {
          return {
            ...item,
            x: minX < 0 ? item.x + Math.abs(minX) : item.x,
            y: minY < 0 ? item.y + Math.abs(minY) : item.y,
            children: item.children || []
          }
        })
        this.$refs.topologyMap.pageInit(nodeList)
      }).catch(err => {
        this.$TZmessage.error(err.msg || '获取节点失败')
      }).finally(() => {
        // this.loading = false
      })
    },
    cancel() {
      Object.keys(this.deviceItem).forEach(key => {
        this.deviceItem[key] = ''
      })
      this.editVisible = false
    },
    deleteNode({id}) {
      this.loading = true
      this.$http.delete(`/topu/deleteNode/${id}`).then(() => {
        this.queryLocalNodeTree()
      }).catch(err => {
        this.$TZmessage.error(err.msg || '删除节点失败')
      }).finally(() => {
        this.loading = false
      })
    },
    filterNode(value, data) {
      if (!value) {return true;}
      return data.internetName.indexOf(value) !== -1;
    },
    queryNodeTree() {
      return this.$http.get('/topu/getInterNodeTreeAll', {
        params: {
          network: this.network
        }
      }).then(res => {
        this.nodeTree = res || []
        const first = this.nodeTree[0]
        if (first) {
          this.nodeClick(first)
        }
      }).catch(err => {
        this.$TZmessage.error(err.msg || '获取节点失败')
      })
    },
  },
  watch: {
    filterText(val) {
      this.$refs.nodeTree.filter(val);
    }
  },
  mounted() {
    this.refresh()
  }
}
</script>

<style lang="scss" scoped>
    ::v-deep .parent-tips,.child-tips {
        background-color: #2A3FCF;
        color: #ffffff;
        box-sizing: border-box;
        padding: 4px 0;
        border-radius: 5px 0 0 5px;
        z-index: 0;
        max-height: 600px;
        overflow: auto;
        .no-data {
            font-size: 12px;
            transform: scale(0.8);
        }
        .item-title {
            line-height: 30px;
            font-size: 12px;
            font-weight: bolder;
            padding-left: 4px;
        }
        .tip-item {
            user-select: none;
            margin: 4px;
            background: rgba(255,255,255,.1);
            border-radius: 4px;
            padding: 4px;
            cursor: pointer;
            .device-icon {
                cursor: copy;
                width: 30px;
                height: 30px;
                background-color: #ffffff;
                border-radius: 50%;
            }
            .icon-name {
                text-align: left;
                flex: 1;
                transform: scale(0.8);
            }
            &.can-drag {
                cursor: move;
            }
        }
    }
    .page {
        padding-right: 0;
        .left-container {
            padding-right: 16px;
            border-right: 1px solid #DCDFE6;
        }
        .right-container {
            padding-left: 16px;
            position: relative;
            .tip-container {
                position: fixed;
                top: calc(50vh - 315px);
                right: 0;
            }
            .left-btn {
                position: absolute;
                left: 16px;
                top: 0;
                z-index: 99;
            }
        }
        .custom-tree-node {
            width: 100%;
            height: 100%;

        }
        .select-node {
            background: $primary;
            color: #ffffff;
        }
        .error-msg {
            color: $color-warning;
            font-size: 12px;
        }
    }
</style>


